#include <list>
#include <functional>
#include <iostream>
#include <algorithm>
namespace my_fun{
//class mem_fun_t    
template<typename R, typename T> class mem_fun_t:
    public std::unary_function<T*,R>
{
    R (T::*pmf)();
public:
    explicit mem_fun_t(R (T::*p)()):pmf(p){}
    R operator()(T* p)const{return (p->*pmf)();}
};
//class const_mem_fun_t
template<typename R, typename T> class const_mem_fun_t:
    public std::unary_function<T*, R>
{
    R (T::*pmf_)()const;
public:
    explicit const_mem_fun_t<R, T>( R (T::*pmf)()const):pmf_(pmf){}
    R operator()(const T* p)const{return (p->*pmf_)();}
};
//class mem_fun1_t
template<typename R, typename T, typename Targ> class mem_fun1_t:
    public std::binary_function<T*, Targ, R>
{
    R (T::*pmf_)(Targ);
public:
    explicit mem_fun1_t(R (T::*pmf)(Targ)):pmf_(pmf){}
    R operator()(T* p, Targ arg)const{return (p->*pmf_)(arg);}
};
//class const_mem_fun1_t
template<typename R, typename T, typename Targ> class const_mem_fun1_t:
    public std::binary_function<const T*, Targ, R>
{
    R (T::*pmf_)(Targ)const;
public:
    explicit const_mem_fun1_t<R, T, Targ> (R (T::*pmf)(Targ)const):pmf_(pmf){}
    R operator()(const T* p, Targ arg)const{
        return (p->*pmf_)(arg);
    }
};
//mem_fun
template<typename R , typename T> mem_fun_t<R,T> mem_fun(R (T::*f)())
{
    return mem_fun_t<R, T>(f);
}
template<typename R, typename T, typename Targ>
const_mem_fun1_t<R, T, Targ> mem_fun(R (T::*pmf)(Targ)const)
{
    return const_mem_fun1_t<R, T, Targ>(pmf);
}
template<typename R, typename T, typename Targ>
mem_fun1_t<R, T, Targ> mem_fun(R(T::*pmf)(Targ))
{
    return mem_fun1_t<R, T, Targ>(pmf);
}
template<typename R, typename T>
const_mem_fun_t<R, T> mem_fun(R (T::*pmf)()const)
{
    return const_mem_fun_t<R,T>(pmf);
}




//reference to member function

//class mem_fun_ref_t
template<typename R, typename T> class mem_fun_ref_t:
    public std::unary_function<T,R>
{
    R (T::*pmf)();
public:
    explicit mem_fun_ref_t(R (T::*p)()):pmf(p){}
    R operator()(T& p)const{return (p.*pmf)();}
};
//class const_mem_fun_ref_t
template<typename R, typename T> class const_mem_fun_ref_t:
    public std::unary_function<T,R>
{
    R (T::*pmf)()const;
public:
    explicit const_mem_fun_ref_t(R (T::*p)()const):pmf(p){}
    R operator()(const T& p)const{return (p.*pmf)();}
};
//class mem_fun1_ref_t
template<typename R, typename T, typename Targ> class mem_fun1_ref_t:
    public std::binary_function<T, Targ, R>
{
    R (T::*pmf)(Targ);
public:
    explicit mem_fun1_ref_t(R (T::*p)(Targ)):pmf(p){}
    R operator()(T& p, Targ arg)const{return (p.*pmf)(arg);}
};
//class const_mem_fun1_ref_t
template<typename R, typename T, typename Targ>
class const_mem_fun1_ref_t:public std::binary_function<T, Targ, R>
{
    R (T::*pmf_)(Targ)const;
public:
    explicit const_mem_fun1_ref_t(R (T::*p)(Targ) const):pmf_(p){}
    R operator()(const T& p, Targ arg)const{
        return (p.*pmf_)(arg);
    }
};

//mem_ref
template<typename R, typename T, typename Targ>
mem_fun1_ref_t<R, T, Targ> mem_fun_ref(R (T::*f)(Targ))
{
    return mem_fun1_ref_t<R, T, Targ>(f);
}
template<typename R, typename T>
mem_fun_ref_t<R,T> mem_fun_ref(R (T::*f)())
{
    return mem_fun_ref_t<R,T>(f);
}
template<typename R, typename T, typename Targ>
const_mem_fun1_ref_t<R, T, Targ> mem_fun_ref(R (T::*f)(Targ)const)
{
    return const_mem_fun1_ref_t<R, T, Targ>(f);
}
template<typename R, typename T> const_mem_fun_ref_t<R,T> mem_fun_ref(R (T::*f)()const)
{
    return const_mem_fun_ref_t<R,T>(f);
}

}//end namespace my_fun

class base
{
public:
    void print(){
        std::cout << "Just for test:" << this << std::endl;
    }
    void print_with(const std::string str){
        std::cout << "Just for test: " << this << str << std::endl;
    }
    void print_const()const{
        std::cout << "Const function for test" << std::endl;
    }
    void print_with_const(const std::string str)const{
        std::cout << "Const function for test WITH" << std::endl;
    }
    
            
};
int main()
{
    
    //for pointer
    std::list<base*> lsp;
    lsp.push_back(new base());
    lsp.push_back(new base());
    lsp.push_back(new base());
    for_each(lsp.begin(), lsp.end(), my_fun::mem_fun(&base::print));
    for_each(lsp.begin(), lsp.end(),
           std::bind2nd(my_fun::mem_fun(&base::print_with), "TEST"));

    //for const point
    std::list<const base*> lsp1;
    lsp1.push_back(new base());
    lsp1.push_back(new base());
    lsp1.push_back(new base());
    for_each(lsp1.begin(), lsp1.end(),my_fun::mem_fun(&base::print_const));
    for_each(lsp1.begin(), lsp1.end(),
          std::bind2nd(my_fun::mem_fun(&base::print_with_const), "TESTTEST"));

             


    //for ref

    std::list<base> lsp2;
    lsp2.push_back(base());
    lsp2.push_back(base());
    lsp2.push_back(base());

    for_each(lsp2.begin(), lsp2.end(), my_fun::mem_fun_ref(&base::print));
    for_each(lsp2.begin(), lsp2.end(), my_fun::mem_fun_ref(&base::print_const));
    

    
    //for const ref
    for_each(lsp2.begin(), lsp2.end(),
             std::bind2nd(my_fun::mem_fun_ref(&base::print_with), "TEST_BY_REF"));
             
    for_each(lsp2.begin(), lsp2.end(),
           std::bind2nd(my_fun::mem_fun_ref(&base::print_with_const), "TEST_BY_REF"));
           
}



